/* 
 * Enterprise Library Extensions - DI + ORM.
 * Copyright (C) 2006
 *
 * GNU General Public License
 *
 * This program is free software; you can redistribute 
 * it and/or modify it under the terms of the GNU General Public License 
 * as published by the Free Software Foundation; either version 2 of the 
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful, but 
 * WITHOUT ANY WARRANTY; without even the implied warranty of 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 
 * See the GNU General Public License for more details.
 */
using System;
using System.Data;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using ELE.EnterpriseLibrary.Data;

namespace ELE.EnterpriseLibrary.Data
{
  /// <summary>
  /// A generic implementation of <see cref="ISqlParser"/>.
  /// </summary>
  public class GenericSqlParser : ISqlParser
  {
    /// <summary>
    /// String that is used to represent the "where" clause of a sql statement
    /// </summary>
    protected const string WHERE_CLAUSE = "where";
    /// <summary>
    /// String that is used to represent the "select" clause of a sql statement
    /// </summary>
    protected const string SELECT_CLAUSE = "select";
    /// <summary>
    /// String that is used to represent the "from" clause of a sql statement
    /// </summary>
    protected const string FROM_CLAUSE = "from";
    /// <summary>
    /// String that is used to represent an identifier of an input parameter 
    /// in a sql statement (i.e. "@" defines params as "@ParamName")
    /// </summary>
    protected const string IDENTIFIER = "@";
    /// <summary>
    /// A string that represents a separator used as a delimiter in columns, tables and parameter names.
    /// </summary>
    protected const string SEPARATOR = ",";
    /// <summary>
    /// Empty constructor that facilitates this class to be built using reflection.
    /// </summary>
    public GenericSqlParser() { }

    #region ISqlParser Members
    /// <summary>
    /// Implementation of the interface method.
    /// <para>For more information refer to <see cref="ISqlParser.GetInputParameterNames(string)"/></para>
    /// </summary>
    /// <param name="sqlText"></param>
    /// <returns></returns>
    public virtual List<string> GetInputParameterNames(string sqlText)
    {
      List<string> retVal = new List<string>();
      Regex regex = new Regex(@"\s*@\w*\s*");
      foreach (Match m in regex.Matches(sqlText))
      {
        retVal.Add(m.Value.Trim());
      }
      return retVal;
    }
    /// <summary>
    /// Implementation of the interface method.
    /// <para>For more information refer to <see cref="ISqlParser.GetOutputParameterNames(string)"/></para>
    /// </summary>
    /// <param name="sqlText"></param>
    /// <returns></returns>
    public virtual List<string> GetOutputParameterNames(string sqlText)
    {
      int firstSelectIndex = sqlText.IndexOf(SELECT_CLAUSE);
      string sub = sqlText.Substring(firstSelectIndex + SELECT_CLAUSE.Length);
      int secondSelectIndex = sub.IndexOf(SELECT_CLAUSE);
      int firstFromIndex = sqlText.IndexOf(FROM_CLAUSE);
      List<string> rVal = new List<string>();
      if (firstFromIndex < secondSelectIndex)
      {
        while (sub.IndexOf(SEPARATOR) >= 0 && sub.IndexOf(FROM_CLAUSE) > sub.IndexOf(SEPARATOR))
        {
          string pName = sub.Substring(0, sub.IndexOf(SEPARATOR));
          int indxOfAs = pName.ToLower().IndexOf(" as ");
          if (indxOfAs >= 0)
          {
            pName.Substring(indxOfAs + " as ".Length);
          }
          rVal.Add(pName);
          sub = sub.Substring(sub.IndexOf(SEPARATOR) + 1);
        }
      }
      else
      {
        // This is complicated
        return null;
      }
      return rVal;
    }
    /// <summary>
    /// Implementation of the interface method.
    /// <para>For more information refer to <see cref="ISqlParser.GetOutputParameterNames(string)"/></para>
    /// <para>Types supported by this class are (case insensitive):</para>
    /// <list type="bullet">
    ///   <item>int, integer</item>
    ///   <item>string, varchar</item>
    ///   <item>double</item>
    ///   <item>date</item>
    ///   <item>datetime</item>
    ///   <item>long</item>
    ///   <item>short</item>
    /// </list>
    /// </summary>
    /// <param name="typeString"></param>
    /// <returns></returns>
    public virtual DbType GetDbTypeFromString(string typeString)
    {
      switch (typeString.ToLower())
      {
        case "int":
        case "integer":
          return DbType.Int32;
        case "string":
        case "varchar":
          return DbType.String;
        case "double":
          return DbType.Double;
        case "date":
          return DbType.Date;
        case "datetime":
          return DbType.DateTime;
        case "long":
          return DbType.Int64;
        case "short":
          return DbType.Int16;
      }
      return DbType.String;
    }
    /// <summary>
    /// Implementation of the interface method.
    /// <para>For more information refer to <see cref="ISqlParser.GetNullableObjectValue(string, string)"/></para>
    /// </summary>
    /// <param name="nullableValue"></param>
    /// <param name="typeString"></param>
    /// <returns></returns>
    public object GetNullableObjectValue(string nullableValue, string typeString)
    {
      switch (this.GetDbTypeFromString(typeString))
      {
        case DbType.Int32:
          return Int32.Parse(nullableValue);
        case DbType.Int64:
          return Int64.Parse(nullableValue);
        case DbType.Int16:
          return Int16.Parse(nullableValue);
        case DbType.String:
          return nullableValue;
        case DbType.Double:
          return Double.Parse(nullableValue);
        case DbType.DateTime:
        case DbType.Date:
          if (nullableValue.ToLower().Trim() == "datetime.minvalue")
          {
            return DateTime.MinValue;
          }
          else if (nullableValue.ToLower().Trim() == "datetime.maxvalue")
          {
            return DateTime.MaxValue;
          }
          return DateTime.Parse(nullableValue);
      }
      return nullableValue;
    }
    /// <summary>
    /// Implementation of the method that build an input param name from the settings in configuration
    /// </summary>
    /// <param name="paramName"></param>
    /// <returns></returns>
    public virtual string BuildInputParameterName(string paramName)
    {
      return paramName;
    }
    #endregion
  }
}
